package com.nifostasky.controller;

import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.nifostasky.config.CustomHystrixCommand;
import com.nifostasky.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@RestController
@RequestMapping("/restTemplate")
public class RestTemplateController {
    @Autowired
    private RestTemplate restTemplate;

    //服务方地址
    //格式: http://ip地址:调用服务的端口/[服务提供者controller类上的RequestMapping/]
    private static final String URL = "http://127.0.0.1:8888/student/queryUserById/";

    //通过Eureka注册中心服务的 服务名 来调用服务
    //格式: http://[服务对应服务名]/student/queryUserById/
    private static final String URLEureka = "http://springcloud-controller/student/queryUserById/";

    //至少需要4000ms才能响应的服务;
    private static final String URLEureka4000ms="http://springcloud-controller/student/queryUserById4000ms/";

    //服务提供者提供的异常服务
    private static final String URLEurekaThrowable="http://springcloud-controller/student/producerThrowable/";


    /**
     * rest方式消费服务;
     */
    @GetMapping("/consumerRestTemplate/{id}")
    public Student consumerRestTemplate(@PathVariable("id")Integer id){
        return restTemplate.getForObject(URL + id , Student.class);
    }

    /**
     * rest方式消费服务,且通过 Ribbon 实现负载均衡
     */
    @GetMapping("/ribbonRestTemplate/{id}")
    public Student ribbonRestTemplate(@PathVariable("id")Integer id){
        return restTemplate.getForObject(URLEureka + id , Student.class);
    }

    /**
     * 启用熔断器功能 Hystrix
     */
    @GetMapping("/hystrixRestTemplate/{id}")
    //当发生熔断，回调error方法
    @HystrixCommand(fallbackMethod = "error")
    public Student hystrixRestTemplate(@PathVariable("id")Integer id){
        return restTemplate.getForObject(URLEureka + id , Student.class);
    }

    /**
     * 测试超时熔断;
     */
    @GetMapping("/hystrixOutTimeRestTemplate/{id}")
    //当发生熔断，回调error方法,通过commandProperties属性设置超时时间
    @HystrixCommand(fallbackMethod = "errorThrowable",
                    commandProperties={ @HystrixProperty(
                        name="execution.isolation.thread.timeoutInMilliseconds",
                        value="1500")})
    public Student hystrixOutTimeRestTemplate(@PathVariable("id")Integer id){
        return restTemplate.getForObject(URLEureka4000ms + id , Student.class);
    }

    /**
     * 消费者服务本身发生异常;
     */
    @GetMapping("/consumerThrowable/{id}")
    //当发生熔断，回调error方法,通过commandProperties属性设置超时时间1s,
    @HystrixCommand(fallbackMethod = "errorThrowable",
            commandProperties={ @HystrixProperty(
                    name="execution.isolation.thread.timeoutInMilliseconds",
                    value="10000")})
    public Student consumerThrowable(@PathVariable("id")Integer id){
        //消费者服务 故意提供的错误,;
        Integer age = 23/0;
        return restTemplate.getForObject(URLEureka + id , Student.class);
    }

    /**
     * 服务提供者发生异常;
     */
    @GetMapping("/producerThrowable/{id}")
    //当发生熔断，回调error方法,通过commandProperties属性设置超时时间1s,
    @HystrixCommand(fallbackMethod = "errorThrowable",
            commandProperties={ @HystrixProperty(
                    name="execution.isolation.thread.timeoutInMilliseconds",
                    value="10000")})
    public Student producerThrowable(@PathVariable("id")Integer id){
        return restTemplate.getForObject(URLEurekaThrowable + id , Student.class);
    }

    /**
     * 忽略异常,直接将异常抛给前端; 而不进行 服务降级的fallback回调;
     * @HystrixCommand(ignoreExceptions = Exception.class) 捕获所有异常;
     */
    @GetMapping("/ignoreThrowable/{id}")
    //当发生熔断，回调error方法,通过commandProperties属性设置超时时间1s,
    @HystrixCommand(fallbackMethod = "errorThrowable",
            ignoreExceptions = Exception.class,
            commandProperties={ @HystrixProperty(
                    name="execution.isolation.thread.timeoutInMilliseconds",
                    value="10000")})
    public Student ignoreThrowable(@PathVariable("id")Integer id){
        return restTemplate.getForObject(URLEurekaThrowable + id , Student.class);
    }


    /**
     * 发生熔断时调用本方法;
     * 这个回调方法的 返回值,参数列表 必须与 对应接口的 返回值,参数列表 保持一致;
     *  需要将请求的接口的参数 加入到 回调方法的参数中;
     *      比如这里 请求的参数: id -> Integer id;
     *                    返回值:   Student -> Student;
     */
    private Student error(Integer id){
        System.out.println("====================== Hystrix: error ==============================");
        return null;
    }
    /**
     * 发生异常导致熔断时调用本方法;
     * 这个回调方法的 返回值,参数列表 必须与 对应接口的 返回值,参数列表 保持一致;
     * 而且需要添加一个异常参数 Throwable e
     *  需要将请求的接口的参数 加入到 回调方法的参数中;
     *      比如这里 请求的参数: id -> Integer id; Throwable e;
     *                    返回值:   Student -> Student;
     */
    private Student errorThrowable(Integer id , Throwable e){
        System.out.println("====================== Hystrix: error {"+id+"}==============================");
        System.out.println("============================================================");
        System.out.println(e.getMessage());
        System.out.println("============================================================");
        return null;
    }


    /**
     * 自定义熔断器,同步调用;
     */
    @GetMapping("/synchronizationCustomHystrix/{id}")
    public Student synchronizationCustomHystrix(@PathVariable("id")Integer id){
        //自定义熔断器
        CustomHystrixCommand customHystrixCommand = new CustomHystrixCommand(
                com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(""))
                , restTemplate
                , id);
        //同步调用;
        Student student = customHystrixCommand.execute();
        return student;
    }

    /**
     * 自定义熔断器,异步调用;
     */
    @GetMapping("/asynchronousCustomHystrix/{id}")
    public Student asynchronousCustomHystrix(@PathVariable("id")Integer id) throws ExecutionException, InterruptedException {
        //自定义熔断器
        CustomHystrixCommand customHystrixCommand = new CustomHystrixCommand(
                com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(""))
                , restTemplate
                , id);
        //异步调用;
        Future<Student> queue = customHystrixCommand.queue();
        //如果没有获取到 值;则一直阻塞;
        Student student = queue.get();
        return student;
    }
}
